package com.liu.auth.controller;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.liu.auth.feign.api.ShopCarServices;
import com.liu.auth.feign.api.UserServices;
import com.liu.shop.common.constant.AuthConstants;
import com.liu.shop.common.constant.RabbitConstant;
import com.liu.shop.common.constant.RedisConstatns;
import com.liu.shop.common.exception.BusinessException;
import com.liu.shop.common.utils.R;
import com.liu.shop.common.utils.TokenUtlis;
import com.liu.shop.common.vo.Email;
import com.liu.shop.common.vo.SmsVo;
import com.liu.shop.common.vo.UserVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/auth")
@Slf4j
public class AuthController {
    @Autowired
    private UserServices userServices;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ShopCarServices shopCarServices;


    /**
     * 根据用户输入的名字判断是否存在
     * @param filed
     * @param value
     * @return
     */
    @PostMapping("/check")
    public Map<String, Object> findByName(@RequestParam("name") String filed, @RequestParam("param") String value){
        Map<String,Object> map = new HashMap();
        //如果用户没有输入用户名就发送了请求，返回提示
        if (StringUtils.isEmpty(filed)){
            map.put("status","n");
            map.put("info","字段名称为空！");
            return map;
        }
        if (StringUtils.isEmpty(value)){
            map.put("status","n");
            map.put("info","用户名为空！");
            return map;
        }
        // 1.查询用户是否已进被注册
        UserVo byName = userServices.findByName(filed, value);
        if(byName == null){
            map.put("status","y");
            map.put("info","可以使用");
            return map;
        }else{
            map.put("status","n");
            if("username".equals(filed)){
                map.put("info","该用户已被注册！");
            }else if("email".equals(filed)){
                map.put("info","该邮箱已注册过！");
            }
        }
        return map;
    }

    /**
     * 考虑到多个用户同时注册时会混乱验证码，所以要将手机号和验证码进行绑定，
     * 并将验证码存入服务端（redis）设置过期时间
     *
     * @param mobile
     * @return
     */
    @GetMapping("/sendPhoneCode/{mobile}")
    public void sendPhoneCode(@PathVariable String mobile){
        //1 .判断手机号非空，为空就让全局异常处理
        if (StringUtils.isEmpty(mobile)) {
            throw new BusinessException("手机号不能为空", 001);
        }

        // 2.生成验证码
        String code = RandomStringUtils.random(6, false, true);

        // 3.在服务端保存验证码，reids,并设置过期时间60秒
        String key = RedisConstatns.AUTH_USER_REGISTER_PRE + mobile;
        redisTemplate.opsForValue().set(key, code, 60, TimeUnit.SECONDS);

        // 4.向手机发送短信验证码
        SmsVo smsVo = new SmsVo();
        smsVo.setPhone(mobile);
        smsVo.setCode(code);
        System.out.println(smsVo);
        rabbitTemplate.convertAndSend(RabbitConstant.INFORM_EXCHANGE,"sms.send",smsVo);
    }

    /**
     * 注册功能
     * @param userVo
     * @return
     */
    @RequestMapping("/register")
    public R register(@RequestBody UserVo userVo){
        log.debug("userVo:{}",userVo);

        // 获取用户输入的验证码
        String code = userVo.getCode();
        // 查询redis中的验证码
        String key = RedisConstatns.AUTH_USER_REGISTER_PRE+userVo.getPhone();
        String redisCode = redisTemplate.opsForValue().get(key);
        // 验证码判断
        if(StringUtils.isEmpty(redisCode)){
            throw new BusinessException("验证码已经过期，请重新获取",502);
        }
        if(!redisCode.equals(code)){
            throw new BusinessException("验证码输入有误！",502);
        }

        // 对用户的密码进行加密
        userVo.setPassword(BCrypt.hashpw(userVo.getPassword(),BCrypt.gensalt()));

        R r = userServices.register(userVo);

        return R.ok();
    }

    /**
     * 登录验证
     * @param userVo
     * @param httpSession
     * @return
     */
    @PostMapping("/login")
    public R login(@RequestBody UserVo userVo, HttpSession httpSession) {

        log.debug("sessionClass:" + httpSession.getClass());
        // 1.根据用户名查询数据
        UserVo dbUserVo = userServices.findByName("username", userVo.getUsername());
        if (dbUserVo == null) {
            throw new BusinessException("用户名不存在", 500);
        }
        // 2.做密码的比对
        try {
            boolean checkpw = BCrypt.checkpw(userVo.getPassword(), dbUserVo.getPassword());
            if (!checkpw) {
                throw new BusinessException("用户名或密码错误", 500);
            }
        } catch (Exception e) {
            log.error("用户密码比对失败", e);
            throw new BusinessException("用户名或密码错误", 500);
        }
        // 3.登录成功
        dbUserVo.setPassword(null);
        httpSession.setAttribute(AuthConstants.LOGIN_USER, dbUserVo);

        // 合并购车,合并成功后回到之前的页面
        shopCarServices.mergeCar(dbUserVo.getId());

        // 4.响应成功
        return R.ok();
    }

    /**
     * 用户退出登录
     * @param session
     * @return
     */
    @GetMapping("/logout")
    public R logout(HttpSession session) {
        session.invalidate(); // 注销session，也会删除redis的session
        return R.ok();
    }

    /**
     * 前端用户登录状态模板，查询用户信息
     * @param session
     * @return
     */
    @GetMapping("/getUser")
    public R getUser(HttpSession session, HttpServletRequest request) {

        // 1.先获取当前登录用户
        UserVo userVo = (UserVo) session.getAttribute(AuthConstants.LOGIN_USER);

        // 2.调用购物车模块，获取用户购物车商品数量
        Integer count = shopCarServices.getUserCarCount();

        // 3.创建一个Map
        Map<String, Object> map = new HashMap<>();
        // 将购物车商品数量存入到map中，并响应给前端
        map.put("userCarCount", count);
        if (userVo != null) {
            map.put("data", userVo);
        }
        return R.ok(map);
    }

    /**
     * 用户忘记密码
     * @param map
     * @return
     */
    @PostMapping("/queryUser")
    public R queryUser(@RequestBody Map<String ,Object> map){
        log.debug("name:{}", map);

        if(map!=null&&map.isEmpty()){
            throw new BusinessException("用户名不能为空", 500);
        }
        String name = map.get("username") + "";
        if (StringUtils.isEmpty(name)) {
            throw new BusinessException("用户名不能为空", 500);
        }
        // 查询用户是否存在
        UserVo userVo = userServices.queryUser(name);
        if (userVo == null) {
            throw new BusinessException("用户不存在", 500);
        }

        // 获取用户的邮箱地址
        String email = userVo.getEmail();

        String msg = "修改密码的连接已经发送到您【%s】的邮箱，请<a href='%s'>登录</a>邮箱查看";


        // 1.获取带有*的邮箱
        String hidenEmail = getHidenEmail(email);

        // 2.获取邮箱的登录地址
        String emailLongUrl = getEmailLongUrl(email);

        // 3.格式化
        String formatMsg = String.format(msg, hidenEmail, emailLongUrl);

        // 发送邮件,将用户的所有信息发送，并存入token中
        sendEmail(email, userVo);

        // 4.响应用户
        return R.ok().put("msg", formatMsg);


    }

    /**
     * 发送email
     * @param e
     * @param userVo
     */
    private void sendEmail(String e, UserVo userVo) {
        // 调用token方法创建token,设置过期时间
        Map<String ,String> map =new HashMap<>();
        map.put("id",userVo.getId().toString());
        String token = TokenUtlis.creatToken(map,1000*60*30);
        String url = "http://localhost:81/auth/changePassword.html?token="+token;
       Email email = new Email();
       email.setTitle("商城用户密码修改");
       email.setContent("点击<a href ='"+ url +"'>这里</a>修改密码,连接30分钟内有效，请尽快修改。");
       email.setToUserList(e);

       // 发送到MQ中
        rabbitTemplate.convertAndSend(RabbitConstant.INFORM_EXCHANGE,"email.send",email);
    }

    /**
     * 隐藏用户输入的邮箱信息
     * @param email
     * @return
     */
    public String getHidenEmail(String email) {
        String subEmail = email.substring(4, email.lastIndexOf("@"));
        String replace = email.replace(subEmail, "*****");
        return replace;
    }

    /**
     * 切割不同的邮箱地址
     * @param email
     * @return
     */
    public String getEmailLongUrl(String email) {
        String url = "http://www.mail.%s.com";
        String substring = email.substring(email.lastIndexOf("@") + 1, email.lastIndexOf("."));
        return String.format(url, substring);
    }

    /**
     * 修该密码
     * @param map
     * @return
     */
    @PostMapping("/changePwd")
    public R changePwd(@RequestBody Map<String,String> map){
        // 对前端传来的数据进行非空校验
        String token = map.get("token");
        String password = map.get("password");
        if(map==null&& map.isEmpty()){
            return R.error(500, "map为空");
        }
        if(password==null&& StringUtils.isEmpty(password)){
            return R.error(500, "密码为空");
        }
        if(token==null&&StringUtils.isEmpty(token)){
            return R.error(500, "token为空");
        }
        // 校验token
        DecodedJWT decodedJWT = TokenUtlis.checkToken(token);
        if (decodedJWT == null) {
            return R.error(500, "token校验失败");
        }
        // 3.从Token中获取userId
        String id = TokenUtlis.getData(decodedJWT, "id");

        // 4.查询这个id是否存在
        UserVo userVo = userServices.findByName("id", id);
        if (userVo == null) {
            return R.error(500, "用户不存在");
        }
        // 5.对密码加密
        userVo.setPassword(BCrypt.hashpw(password,BCrypt.gensalt()));

        // 6.修改密码
       userServices.updatePwd(userVo);

        return R.ok();
    }

}
